import test from "ava"
import type { LiteralUnion, Required, Primitive } from "../src/types/common"
import type { Mutable, ObjectEntries } from "../src/utils/types"
import type { CenoOptions, Options, HttpMethod, SearchParamsOption, Progress} from "../src/types/options"
import type { CenoResponse } from "../src/types/response"
import type { RetryOptions } from "../src/types/retry"


test('类型 - Primitive 原始类型', (t) => {
    // 编译时类型检查 - 这些应该都能正确分配
    const nullValue: Primitive = null
    const undefinedValue: Primitive = undefined
    const stringValue: Primitive = 'test'
    const numberValue: Primitive = 123
    const booleanValue: Primitive = true
    const symbolValue: Primitive = Symbol('test')
    const bigintValue: Primitive = BigInt(123)
    
    // 运行时验证
    t.is(nullValue, null)
    t.is(undefinedValue, undefined)
    t.is(stringValue, 'test')
    t.is(numberValue, 123)
    t.is(booleanValue, true)
    t.is(typeof symbolValue, 'symbol')
    t.is(typeof bigintValue, 'bigint')
})

test('类型 - Required 工具类型', (t) => {
    interface TestInterface {
        required: string
        optional?: number
        nullable?: string | null
    }
    
    // 全部必选
    type AllRequired = Required<TestInterface>
    
    // 编译时验证：这些赋值应该都是有效的
    const allRequired: AllRequired = {
        required: 'test',
        optional: 123,
        nullable: 'value'
    }
    
    // 部分必选
    type PartialRequired = Required<TestInterface, 'optional'>
    
    const partialRequired: PartialRequired = {
        required: 'test',
        optional: 456, // 现在是必选的
        // nullable 仍然是可选的
    }
    
    t.is(allRequired.required, 'test')
    t.is(allRequired.optional, 123)
    t.is(partialRequired.optional, 456)
})

test('类型 - LiteralUnion 字面量联合', (t) => {
    // HTTP 方法的字面量联合类型
    type HttpMethodUnion = LiteralUnion<'GET' | 'POST', string>
    
    // 应该接受字面量
    const method1: HttpMethodUnion = 'GET'
    const method2: HttpMethodUnion = 'POST'
    
    // 应该接受任意字符串
    const method3: HttpMethodUnion = 'CUSTOM'
    const method4: HttpMethodUnion = 'WebDAV'
    
    t.is(method1, 'GET')
    t.is(method2, 'POST')
    t.is(method3, 'CUSTOM')
    t.is(method4, 'WebDAV')
})

test('类型 - Mutable 可变类型', (t) => {
    interface ReadonlyInterface {
        readonly id: number
        readonly name: string
        readonly tags: readonly string[]
    }
    
    type MutableInterface = Mutable<ReadonlyInterface>
    
    const mutableObj: MutableInterface = {
        id: 1,
        name: 'test',
        tags: ['tag1', 'tag2']
    }
    
    // 现在可以修改属性
    mutableObj.id = 2
    mutableObj.name = 'updated'
    mutableObj.tags = [...mutableObj.tags, 'tag3']

    t.is(mutableObj.id, 2)
    t.is(mutableObj.name, 'updated')
    t.deepEqual(mutableObj.tags, ['tag1', 'tag2', 'tag3'])
})

test('类型 - ObjectEntries 对象条目', (t) => {
    interface TestObject {
        name: string
        age: number
        active: boolean
    }
    
    const testObj: TestObject = {
        name: 'test',
        age: 25,
        active: true
    }
    
    // ObjectEntries 应该产生正确的条目类型
    const entries = Object.entries(testObj) as ObjectEntries<TestObject>
    
    t.is(entries.length, 3)
    t.deepEqual(entries[0], ['name', 'test'])
    t.deepEqual(entries[1], ['age', 25])
    t.deepEqual(entries[2], ['active', true])
    
    // 数组类型测试
    const arr = [1, 2, 3]
    const arrEntries = Object.entries(arr) as ObjectEntries<typeof arr>
    
    t.deepEqual(arrEntries[0], ['0', 1])
    t.deepEqual(arrEntries[1], ['1', 2])
    t.deepEqual(arrEntries[2], ['2', 3])
})

test('类型 - CenoOptions 配置选项', (t) => {
    // 完整的配置选项
    const options: CenoOptions = {
        json: { test: 'data' },
        parseJson: (text: string) => JSON.parse(text),
        stringifyJson: (data: unknown) => JSON.stringify(data),
        searchParams: { page: 1, limit: 10 },
        prefixUrl: 'https://api.example.com',
        retry: { limit: 3, methods: ['get'] },
        timeout: 5000,
        interceptors: {
            request: [{
                onFulfilled: (req) => req,
                onRejected: (err) => Promise.reject(err)
            }],
            response: [{
                onFulfilled: (res) => res
            }]
        },
        throwHttpErrors: false,
        onDownloadProgress: (progress: Progress) => {
            console.log(`Downloaded: ${progress.percent}%`)
        },
        onUploadProgress: (progress: Progress) => {
            console.log(`Uploaded: ${progress.percent}%`)
        },
        fetch: global.fetch
    }
    
    t.truthy(options.json)
    t.is(typeof options.parseJson, 'function')
    t.is(typeof options.stringifyJson, 'function')
    t.is(options.timeout, 5000)
    t.is(options.throwHttpErrors, false)
})

test('类型 - SearchParamsOption 搜索参数选项', (t) => {
    // 对象格式
    const params1: SearchParamsOption = { page: 1, limit: 10, active: true }
    
    // 字符串格式
    const params2: SearchParamsOption = 'page=1&limit=10'
    
    // URLSearchParams
    const params3: SearchParamsOption = new URLSearchParams('page=1&limit=10')
    
    // 数组格式
    const params4: SearchParamsOption = [['page', '1'], ['tags', 'tag1'], ['tags', 'tag2']]
    
    // 混合类型数组
    const params5: SearchParamsOption = [['page', 1], ['active', true]]
    
    t.is(typeof params1, 'object')
    t.is(typeof params2, 'string')
    t.truthy(params3 instanceof URLSearchParams)
    t.truthy(Array.isArray(params4))
    t.truthy(Array.isArray(params5))
})

test('类型 - Progress 进度类型', (t) => {
    const progress: Progress = {
        percent: 0.5,
        transferredBytes: 512,
        totalBytes: 1024
    }
    
    t.is(progress.percent, 0.5)
    t.is(progress.transferredBytes, 512)
    t.is(progress.totalBytes, 1024)
})

test('类型 - RetryOptions 重试选项', (t) => {
    // 完整配置
    const retryOptions: RetryOptions = {
        limit: 3,
        methods: ['get', 'post'],
        statusCodes: [500, 502, 503],
        afterStatusCodes: [429, 503],
        maxRetryAfter: 30000,
        backoffLimit: 5000,
        delay: (attemptCount: number) => attemptCount * 1000
    }
    
    t.is(retryOptions.limit, 3)
    t.deepEqual(retryOptions.methods, ['get', 'post'])
    t.is(typeof retryOptions.delay, 'function')
    t.is(retryOptions.delay!(2), 2000)
    
    // 最小配置
    const minimalRetry: RetryOptions = {
        limit: 1
    }
    
    t.is(minimalRetry.limit, 1)
})

test('类型 - CenoResponse 响应类型', (t) => {
    // 模拟响应对象（在实际使用中由库提供）
    const mockResponse = new Response('{"test": "data"}', {
        status: 200,
        headers: { 'Content-Type': 'application/json' }
    })
    
    // 添加 json 方法来模拟 CenoResponse
    const cenoResponse = Object.assign(mockResponse, {
        json: async () => ({ test: 'data' })
    }) as CenoResponse<{ test: string }>
    
    t.is(cenoResponse.status, 200)
    t.is(typeof cenoResponse.json, 'function')
})

test('类型 - HttpMethod 方法类型', (t) => {
    const methods: HttpMethod[] = ['get', 'post', 'put', 'delete', 'patch', 'head']
    
    methods.forEach(method => {
        t.is(typeof method, 'string')
        t.true(['get', 'post', 'put', 'delete', 'patch', 'head'].includes(method))
    })
})

test('类型 - Options 继承关系', (t) => {
    // Options 继承了 CenoOptions 和 RequestInit
    const options: Options = {
        // CenoOptions
        json: { test: 'data' },
        timeout: 5000,
        retry: 3,
        
        // RequestInit
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ test: 'data' }),
        cache: 'no-cache',
        credentials: 'include',
        mode: 'cors',
        redirect: 'follow'
    }
    
    t.truthy(options.json)
    t.is(options.method, 'POST')
    t.is(options.timeout, 5000)
    t.is(options.retry, 3)
})

test('类型 - 复杂泛型类型', (t) => {
    // 测试复杂的泛型类型推断
    interface ApiResponse<T> {
        data: T
        status: 'success' | 'error'
        message?: string
    }
    
    interface User {
        id: number
        name: string
        email: string
    }
    
    // 类型应该正确推断
    const userResponse: ApiResponse<User> = {
        data: {
            id: 1,
            name: 'John Doe',
            email: 'john@example.com'
        },
        status: 'success'
    }
    
    const usersResponse: ApiResponse<User[]> = {
        data: [
            { id: 1, name: 'John', email: 'john@example.com' },
            { id: 2, name: 'Jane', email: 'jane@example.com' }
        ],
        status: 'success'
    }
    
    t.is(userResponse.data.id, 1)
    t.is(usersResponse.data.length, 2)
    t.is(usersResponse.data[0].name, 'John')
})

test('类型 - 条件类型判断', (t) => {
    // 测试条件类型的运行时行为
    type IsString<T> = T extends string ? true : false
    
    // 这些在编译时会被解析为字面量类型
    const isStringForString: IsString<string> = true
    const isStringForNumber: IsString<number> = false
    
    t.is(isStringForString, true)
    t.is(isStringForNumber, false)
})

test('类型 - 映射类型转换', (t) => {
    interface Original {
        name: string
        age: number
        active: boolean
    }
    
    // 将所有属性变为可选
    type Partial<T> = {
        [P in keyof T]?: T[P]
    }
    
    const partial: Partial<Original> = {
        name: 'test'
        // age 和 active 是可选的
    }
    
    t.is(partial.name, 'test')
    t.is(partial.age, undefined)
})

test('类型 - 运行时类型检查函数', (t) => {
    // 类型守卫函数
    function isString(value: unknown): value is string {
        return typeof value === 'string'
    }
    
    function isNumber(value: unknown): value is number {
        return typeof value === 'number' && !isNaN(value)
    }
    
    function isArray<T>(value: unknown): value is T[] {
        return Array.isArray(value)
    }
    
    // 测试类型守卫
    const stringValue: unknown = 'test'
    const numberValue: unknown = 123
    const arrayValue: unknown = [1, 2, 3]
    const objectValue: unknown = { test: 'data' }
    
    if (isString(stringValue)) {
        t.is(stringValue.length, 4) // TypeScript 知道这是 string
    }
    
    if (isNumber(numberValue)) {
        t.is(numberValue.toFixed(2), '123.00') // TypeScript 知道这是 number
    }
    
    if (isArray<number>(arrayValue)) {
        t.is(arrayValue.length, 3) // TypeScript 知道这是 number[]
    }
    
    t.true(isString(stringValue))
    t.true(isNumber(numberValue))
    t.true(isArray(arrayValue))
    t.false(isString(objectValue))
})